/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#include "stdafx.h"
#include "DConfigGlobals.h"
#include "Interpreter.h"
#include "CSocketModule.h"
#include "TSocket.h"

///////////////////////////////////
#if defined(USING_MFC) && defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

//===============================================================

VALUE InitSocketModule()
{
	VALUE symtab = AllocFrame(7);
	symtab.Frame()->Add(_T("__module__"), (LPCTSTR)SOCKET_MODULE_NAME);
	symtab.Frame()->Add(_T("socket"), AllocClass(new CSocketImpl()));
	symtab.Frame()->Add(_T("gethostname"), AllocFunction(CSocketImpl::gethostname, (LPCTSTR)SOCKET_MODULE_NAME));
	symtab.Frame()->Add(_T("gethostbyname"), AllocFunction(CSocketImpl::gethostbyname, (LPCTSTR)SOCKET_MODULE_NAME));
	symtab.Frame()->Add(_T("getallhostbyname"), AllocFunction(CSocketImpl::getallhostbyname, (LPCTSTR)SOCKET_MODULE_NAME));
	symtab.Frame()->Add(_T("ping"), AllocFunction(CSocketImpl::ping, (LPCTSTR)SOCKET_MODULE_NAME));
	symtab.Frame()->Add(_T("select"), AllocFunction(CSocketImpl::select, (LPCTSTR)SOCKET_MODULE_NAME));
	symtab.Frame()->Add(_T("SOCK_STREAM"), (int)SOCK_STREAM);
	symtab.Frame()->Add(_T("PF_INET"), (int)PF_INET);
	symtab.Frame()->Add(_T("AF_INET"), (int)AF_INET);
#ifdef AF_IRDA
	symtab.Frame()->Add(_T("AF_IRDA"), (int)AF_IRDA);
#endif
	symtab.Frame()->Add(_T("SOCKET_ERROR"), (int)SOCKET_ERROR);
	return symtab;
}

//===============================================================

// Called by stVoid garbage collection
void freeSocket(void* pVoid) {
	TSocket* lpSock = (TSocket*)pVoid;
	if (lpSock != NULL) {
		lpSock->Close();
		delete lpSock;
	}
}

CSocketImpl::CSocketImpl():CClassImpl(VALUE(), _T("socket"))
{
	m_MemberSymTab = AllocFrame(19);
	// Anything added here is static (becomes shared by all instances of the class)
	m_MemberSymTab.Set(_T("__type__"), _T("socket"));
	m_MemberSymTab.Set(_T("__init__"), AllocFunction(__init__, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("__del__"), AllocFunction(__del__, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("connect"), AllocFunction(connect, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("bind"), AllocFunction(bind, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("listen"), AllocFunction(listen, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("accept"), AllocFunction(accept, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("close"), AllocFunction(close, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("read"), AllocFunction(read, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("readline"), AllocFunction(readline, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("readbin"), AllocFunction(readBinary, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("write"), AllocFunction(write, (LPCTSTR)SOCKET_MODULE_NAME));
	m_MemberSymTab.Set(_T("writeline"), AllocFunction(writeline, (LPCTSTR)SOCKET_MODULE_NAME));
}

CSocketImpl::~CSocketImpl()
{
}

void CSocketImpl::SetException(CInterpreter* pInterp, TSocket* lpSock)
{
	switch (lpSock->m_LastErrorType) {
		case seNone:
			break;
		case seTimeOut:
			pInterp->SetException(_T("etimeout"), lpSock->m_LastErrorMsg, SOCKET_MODULE_NAME, 0, AllocNil());
			break;
		default:
			pInterp->SetException(_T("esocket"), lpSock->m_LastErrorMsg, SOCKET_MODULE_NAME, 0, AllocNil());
			break;
	}
	lpSock->m_LastErrorType = seNone;
	lpSock->m_LastErrorMsg.Empty();
}


VALUE CSocketImpl::__init__(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if ((argCount != 2) || (args[0].Type() != stInteger) || (args[1].Type() != stInteger)) {
		pInterp->SyntaxError(_T("socket.__init__ requires takes two integer args"));
		return AllocNil();
	}
	else {
		TSocket* lpSock = new TSocket();
		lpSock->CreateSocket(args[0].Integer(), args[1].Integer());
		if (lpSock->WasError()) {
			SetException(pInterp, lpSock);
			delete lpSock;
		}
		else {
			self->Set(_T("#lpSock"), AllocVoid(lpSock, freeSocket, _T("socket")));
			self->Set(_T("terminator"), AllocProperty(
				AllocFunction(ReadTerminator, (LPCTSTR)SOCKET_MODULE_NAME),
				AllocFunction(WriteTerminator, (LPCTSTR)SOCKET_MODULE_NAME), AllocNil()));
			self->Set(_T("timeout"), AllocProperty(
				AllocFunction(ReadTimeOut, (LPCTSTR)SOCKET_MODULE_NAME),
				AllocFunction(WriteTimeOut, (LPCTSTR)SOCKET_MODULE_NAME), 0));
		}
	}
	return VALUE();
}

VALUE CSocketImpl::__del__(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	// There shouldn't be any args for the destructor
	ASSERT(argCount == 0);
	// This will force garbage collection of #lpSock to occur if it
	// exists, which will call freeSocket() to close and dispose
	self->Remove(_T("#lpSock"));
	return VALUE();
}

VALUE CSocketImpl::bind(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if ((argCount != 2) || ((args[0].Type() != stString) && (args[1].Type() != stInteger))) {
		pInterp->SyntaxError(_T("socket.bind requires one string and one integer arg"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't bind until the socket constructor has been called yet"));
		return AllocNil();
	}
	else {
		VALUE result = lpSock->Bind(args[0].String(), args[1].Integer());
		if (lpSock->WasError()) SetException(pInterp, lpSock);
		return result;
	}
}

VALUE CSocketImpl::listen(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if ((argCount != 1) || (args[0].Type() !=  stInteger)) {
		pInterp->SyntaxError(_T("socket.listen requires one integer arg"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't accept until the socket constructor has been called yet"));
		return AllocNil();
	}
	else {
		VALUE result = lpSock->Listen(args[0].Integer());
		if (lpSock->WasError()) SetException(pInterp, lpSock);
		return result;
	}
}

VALUE CSocketImpl::accept(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if (argCount != 0) {
		pInterp->SyntaxError(_T("socket.accept requires zero args"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't accept until the socket constructor has been called yet"));
		return AllocNil();
	}
	else {
		TString whoConnected;
		TSocket* s = lpSock->Accept(whoConnected);
		if (lpSock->WasError()) {
			if (s != NULL) delete s;
			SetException(pInterp, lpSock);
			return AllocNil();
		}

		static VALUE vClass = AllocClass(new CSocketImpl());
		VALUE sockInst = AllocClassInstance(vClass, pInterp);
		sockInst.Set(_T("#lpSock"), AllocVoid(s, freeSocket, _T("socket")));

		// Copy the two properties set in socket.__init__ since it won't be called
		sockInst.Set(_T("timeout"), self->Get(_T("timeout")));
		sockInst.Set(_T("terminator"), self->Get(_T("terminator")));

		// Call the Dialect constructor code...
		//VALUE vTemp, vInitFunc = sockInst.Get(_T("__init__"));
		//ASSERT(vInitFunc.Type() == stFunction);
		//pInterp->ExecFuncFromCore(vTemp, vInitFunc, 0, NULL,
		//	&sockInst.ClassInstance()->m_SelfSymTab);

		VALUE result = AllocArray(2);
		result.Set(1, sockInst);
		result.Set(2, whoConnected);
		return result;
	}
}

static bool Build_fds_set(VALUE sockArray, fd_set** s, SOCKET& maxfd, CInterpreter* pInterp)
{
	FD_ZERO(*s);

	TSocket* lpSock;
	if (sockArray.Type() != stArray) {
		pInterp->RuntimeError(_T("Select expected an array of sockets"));
		return false;
	}
	else {
		if (sockArray.Length() == 0) {
			*s = NULL;
		}
		else {
			for (int idx = 1; idx <= sockArray.Length(); ++idx) {
				lpSock = (TSocket*)(sockArray[idx].Get(_T("#lpSock")).Void());
				if (lpSock == NULL) {
					pInterp->RuntimeError(_T("Select called on an invalid socket"));
					return false;
				}

				SOCKET fds = lpSock->fileno();
				maxfd = fds >= maxfd ? fds + 1 : maxfd;
				FD_SET(fds, *s);
			}
		}
	}
	return true;
}

static VALUE BuildSelectResult(VALUE sockArray, fd_set& s)
{
	VALUE vResult = AllocArray(0);
	if (sockArray.Type() == stArray) {
		for (int idx = 1; idx <= sockArray.Length(); ++idx) {
			TSocket* lpSock = (TSocket*)(sockArray[idx].Get(_T("#lpSock")).Void());
			ASSERT(lpSock != NULL);	// Checked and reported earlier
			if (FD_ISSET(lpSock->fileno(), &s)) {
				vResult.Resize(vResult.Length() + 1);
				vResult.Set(vResult.Length(), sockArray[idx]);
			}
		}
	}
	return vResult;
}

VALUE CSocketImpl::select(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	// Don't ASSERT_SELF -- this is a static function

	// NOTE that this version of select will not work properly with socket.readline()
	// because the readline function uses a buffer internal to TSocket which
	// the Winsock select() has no way of checking...

	if (argCount != 4) {
		pInterp->SyntaxError(_T("select requires four args"));
		return AllocNil();
	}

	SOCKET maxfd = 0;	// maxfd isn't used in Winsock version of select
	FD_SET readfds, *pReadfds = &readfds;
	fd_set writefds, *pWritefds = &writefds;
	fd_set excfds, *pExcfds = &excfds;

	// Build the set of file descriptors to wait on...
	if (!Build_fds_set(args[0], &pReadfds, maxfd, pInterp) ||
		!Build_fds_set(args[1], &pWritefds, maxfd, pInterp) ||
		!Build_fds_set(args[2], &pExcfds, maxfd, pInterp)) return AllocNil();

	// Set timeout value...
	timeval timeout, *pTimeout = &timeout;
	if (args[3].Type() == stInteger) {
		timeout.tv_sec = args[3].Integer();
		timeout.tv_usec = 0;
	}
	else if (args[3].Type() == stFloat) {
		timeout.tv_sec = args[3].Integer();
		timeout.tv_usec = (int)((args[3].Float() - (float)timeout.tv_sec) * 1000000.0f);
	}
	else if (args[3].Type() == stNil) {
		pTimeout = NULL;	// Wait indefinitely
	}
	else {
		pInterp->RuntimeError(_T("Invalid timeout value given to select"));
		return AllocNil();
	}

	int result = ::select(maxfd, pReadfds, pWritefds, pExcfds, pTimeout);
	if (result == SOCKET_ERROR) {
		pInterp->RuntimeError(_T("select failed"));	// Should be ESocket? (probably)
		return AllocNil();
	}
	else if (result == 0) {	// Timeout
		VALUE vResult = AllocArray(3);
		vResult.Set(1, AllocArray(0));
		vResult.Set(2, AllocArray(0));
		vResult.Set(3, AllocArray(0));
		return vResult;
	}
	else {
		VALUE vResult = AllocArray(3);
		if (pReadfds == NULL) vResult.Set(1, AllocArray(0));
		else vResult.Set(1, BuildSelectResult(args[0], readfds));
		vResult.Set(2, BuildSelectResult(args[1], writefds));
		vResult.Set(3, BuildSelectResult(args[2], excfds));
		return vResult;
	}
}

VALUE CSocketImpl::connect(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if ((argCount != 2) || ((args[0].Type() != stString) && (args[1].Type() != stInteger))) {
		pInterp->SyntaxError(_T("socket.connect requires one string and one integer arg"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't connect until the socket constructor has been called yet"));
		return AllocNil();
	}
	else {
		lpSock->Connect(args[0].String(), args[1].Integer());
		if (lpSock->WasError()) SetException(pInterp, lpSock);
		return AllocNil();
	}
}

VALUE CSocketImpl::close(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock != NULL) {
		lpSock->Close();
		if (lpSock->WasError()) SetException(pInterp, lpSock);
	}
	return true;
}

VALUE CSocketImpl::gethostname(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	if (argCount != 0) {
		pInterp->SyntaxError(_T("gethostname expects no args"));
		return AllocNil();
	}
	return TSocket::GetHostName();
}

VALUE CSocketImpl::gethostbyname(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	if ((argCount != 1) && (args[0].Type() != stString)) {
		pInterp->SyntaxError(_T("gethostbyname expects one string argument"));
		return AllocNil();
	}
	return TSocket::GetHostByName(args[0].String());
}

VALUE CSocketImpl::getallhostbyname(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	if ((argCount != 1) && (args[0].Type() != stString)) {
		pInterp->SyntaxError(_T("getallhostbyname expects one string argument"));
		return AllocNil();
	}
	return TSocket::GetAllHostByName(args[0].String());
}

VALUE CSocketImpl::write(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if (argCount != 1) {
		pInterp->SyntaxError(_T("socket.write requires one arg"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't send until the socket constructor has been called"));
		return AllocNil();
	}
	else {
		if (args[0].Type() == stChunk) {
			lpSock->Send((char*)args[0].GetChunkData(), args[0].GetChunkByteCount());
		}
		else {
			lpSock->Write(args[0].String());
		}
		if (lpSock->WasError()) SetException(pInterp, lpSock);
		return AllocNil();
	}
}

VALUE CSocketImpl::writeline(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if (argCount != 1) {
		pInterp->SyntaxError(_T("socket.writeline requires one arg"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't send until the socket constructor has been called"));
		return AllocNil();
	}
	else {
		lpSock->WriteLine(args[0].String());
		if (lpSock->WasError()) SetException(pInterp, lpSock);
		return AllocNil();
	}
}

VALUE CSocketImpl::readBinary(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	DWORD numBytes = 4096; // If not specified, read in max of 4K chunks
	if (argCount == 1) {
		if (args[0].Type() != stInteger) {
			pInterp->RuntimeError(_T("If given, socket.readBinary() argument must be integer > 0"));
			return AllocNil();
		}
		numBytes = args[0].Integer();
		if (numBytes <= 0) {
			pInterp->RuntimeError(_T("If given, socket.readBinary() argument must be an integer > 0"));
			return AllocNil();
		}
	}
	else if (argCount != 0) {
		pInterp->RuntimeError(_T("Invalid number of args to socket.readBinary()"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't read until the socket constructor has been called"));
		return AllocNil();
	}
	else {
		VALUE v = AllocNil();
		U8 *buf = new U8[numBytes];
		int byteCount = lpSock->Receive((char*)buf, numBytes);
		if (lpSock->WasError()) {
			SetException(pInterp, lpSock);
		}
		else if (byteCount != 0) { // 0 == connection closed by other end (return nil)
			v = AllocChunk(byteCount, buf);
		}
		delete[] buf;
		return v;
	}
}

VALUE CSocketImpl::read(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if (argCount != 0) {
		pInterp->SyntaxError(_T("socket.read requires no args"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't read until the socket constructor has been called"));
		return AllocNil();
	}
	else {
		TString temp;
		int code = lpSock->Read(temp);
		if (lpSock->WasError()) SetException(pInterp, lpSock);

		if (code == SOCKETRECV_CLOSED) return AllocNil();	// Connection closed by other end
		else return temp;
	}
}

VALUE CSocketImpl::readline(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	if (argCount != 0) {
		pInterp->SyntaxError(_T("socket.readline requires no args"));
		return AllocNil();
	}

	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());
	if (lpSock == NULL) {
		pInterp->RuntimeError(_T("Can't send until the socket constructor has been called"));
		return AllocNil();
	}
	else {
		TString temp;
		int code = lpSock->ReadLine(temp);
		if (lpSock->WasError()) SetException(pInterp, lpSock);

		if (code == SOCKETRECV_CLOSED) return AllocNil();	// Connection closed by other end
		else return temp;
	}
}

VALUE CSocketImpl::ReadTerminator(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	ASSERT(argCount == 1);
	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());

	if (lpSock != NULL) {
		TString term;
		if (lpSock->GetTerminator(term)) {
			return term;
		}
	}
	return AllocNil();
}

VALUE CSocketImpl::WriteTerminator(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	ASSERT(argCount == 2);
	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());

	if (lpSock != NULL) {
		if (args[1].Type() == stString) {
			switch (args[1].Length()) {
				case 0:
					lpSock->SetTerminator();
					break;
				case 1:
					lpSock->SetTerminator((char)args[1].String()[0]);
					break;
				case 2:
					lpSock->SetTerminator2((char)args[1].String()[0], (char)args[1].String()[1]);
					break;
				default:
					pInterp->RuntimeError(_T("Invalid socket.terminator assignment: String more than two characters long"));
					return AllocNil();
			}
		}
		else if (args[1].Type() == stInteger) {
			lpSock->SetTerminator(args[1].Integer());
		}
		else if (args[1].Type() == stNil) {
			lpSock->SetTerminator();
		}
		else {
			pInterp->SyntaxError(_T("Invalid socket.terminator assignment type"));
			return AllocNil();
		}
	}
	if (lpSock->WasError()) SetException(pInterp, lpSock);
	return VALUE();
}

VALUE CSocketImpl::ReadTimeOut(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	ASSERT(argCount == 1);
	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());

	if (lpSock != NULL) {
		int t = lpSock->GetTimeOut();
		if (lpSock->WasError()) SetException(pInterp, lpSock);
		return t;
	}
	return 0;
}

VALUE CSocketImpl::WriteTimeOut(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	ASSERT_SELF((LPCTSTR)SOCKET_MODULE_NAME);

	ASSERT(argCount == 2);
	TSocket* lpSock = (TSocket*)(self->Get(_T("#lpSock")).Void());

	if (lpSock != NULL) {
		if (args[1].Type() != stInteger) {
			pInterp->SyntaxError(_T("socket.timeout requires an integer assignment"));
			return AllocNil();
		}
		else {
			lpSock->SetTimeOut(args[1].Integer());
			if (lpSock->WasError()) SetException(pInterp, lpSock);
		}
	}
	return AllocNil();
}

VALUE CSocketImpl::ping(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
#if defined(__WINNT__) || defined(__WINCE__)
	if ((argCount != 1) || (args[0].Type() != stString)) {
		pInterp->SyntaxError(_T("socket.ping takes one arg (the host to ping)"));
		return AllocNil();
	}
	TSocket ping;
	DWORD time = ping.Ping(args[0].String());
	if (time == PING_ERROR) {
		return false;
	}
	return (int)time;
#else
	pInterp->RuntimeError(_T("Ping is not currently supported on this platform."));
	return 0;
#endif
}
